package com.zang.blogz.aspect;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zang.blogz.annotation.OptLog;
import com.zang.blogz.entity.OperationLog;
import com.zang.blogz.event.OptLogEvent;
import com.zang.blogz.service.OperationLogService;
import io.github.vampireachao.stream.core.optional.Opp;
import com.zang.blogz.utils.IpUtil;
import io.github.vampireachao.stream.core.optional.Sf;
import io.github.vampireachao.stream.core.stream.Steam;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @Author: 橘贝贝
 * @Description:  日志切面
 * @DateTime: 2022/7/21 10:00
 **/
@Aspect
@Component
public class OptLogAspect {

    @Autowired
    private ApplicationContext applicationContext;
    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.zang.blogz.annotation.OptLog)")
    public void optLogPointCut(){

    }

    @AfterReturning(value = "optLogPointCut()",returning = "key")
    public void saveOptLog(JoinPoint joinPoint , Object key){

        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes).resolveReference(RequestAttributes.REFERENCE_REQUEST);
        OperationLog operationLog = new OperationLog();
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取切入点所在的方法
        signature.getDeclaringType().getAnnotation(Api.class);
        // 获取操作
        Api api = (Api) signature.getDeclaringType().getAnnotation(Api.class);
        ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
        OptLog optLog = method.getAnnotation(OptLog.class);
        // 操作模块
        Opp.of(api.tags()[0]).ifPresent(operationLog::setOptModule);
        // 操作类型
        Opp.of(optLog.optType()).ifPresent(operationLog::setOptType);
        // 操作描述
        Opp.of(apiOperation.value()).ifPresent(operationLog::setOptDesc);
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取请求的方法名
        String methodName = method.getName();
        methodName = className + "." + methodName;
        // 请求方式
        operationLog.setRequestMethod(Objects.requireNonNull(request).getMethod());
        // 请求方法
        operationLog.setOptMethod(methodName);
        //JSONUtil.parseObj(JSONUtil.toJsonStr(joinPoint.getArgs())).getStr("id");

        // 请求参数
        operationLog.setRequestParam(JSONObject.toJSONString(joinPoint.getArgs()));
        // 返回结果
        operationLog.setResponseData(JSON.toJSONString(key));

        Opp<Integer> id = Opp.ofTry(() -> (Integer) StpUtil.getExtra("id"));

        Opp.of(id.getException())

                .ifPresent(i->operationLog.setUserId(-1))
                .orElseRun(()-> operationLog.setUserId(id.get()));

        // 请求用户昵称
        Opp<String> nameOpp = Opp.ofTry(() -> (String) StpUtil.getExtra("name"));

        Opp.of(nameOpp.getException())

                .ifPresent((k)->operationLog.setNickname("未登录用户"))
                .orElseRun(()->operationLog.setNickname(nameOpp.get()));
        // 请求IP
        String ipAddress = IpUtil.getIpAddress(request);
        operationLog.setIpAddress(ipAddress);
        operationLog.setIpSource(IpUtil.getIpSource(ipAddress));
        // 请求URL
        operationLog.setOptUrl(request.getRequestURI());

        applicationContext.publishEvent(new OptLogEvent(operationLog));

    }
}
